/*
	ACDM_proc.c

	contains the ACDM kernel process

	Author: Aidan Goddard 23/10/13
*/

#include"../general/driver_error_codes.h"
#include"../../klib/headers/ktypes.h"
#include"../../klib/headers/klib_message_passing.h"
#include"../../klib/headers/klib_proc_threads.h"
#include"../../klib/headers/klib_system_resources.h"

#include"ACDM_buffer.h"
#include"ACDM_window.h"
#include"ACDM_keyboard.h"
#include"ACDM_mod.h"

#include"../../setup/headers/printf.h"

// defines
#define SCR_APP 0
#define SCR_LOGIN 1
#define SCR_HELPW 2
#define SCR_WNDWS 3
#define SCR_PROCS 4
#define SCR_START 5
#define SCR_RESRC 6

// date time defines
#define __BUILD_YEAR ( __BUILD_DATE / 10000)
#define __BUILD_MONTH (( __BUILD_DATE / 100 ) % 100)
#define __BUILD_DAY ( __BUILD_DATE % 100)

//01012014

// external functions
extern void ACDMSpinEnter(uint64_t *lock);
extern void ACDMSpinLeave(uint64_t *lock);
extern void DisableBlink(void);

// global vars
static char *key = "ACDM channel key hello!";
static uint8_t msg_buffer[256];

// control vars (used mainly by the worker for updating views)
static volatile int current_screen = SCR_LOGIN; // start with the login screen
static volatile int pre_lock_screen = SCR_START;
static volatile int pre_help_screen = SCR_LOGIN;
static volatile int help_active = 0;

// data
static int cpu_activity[32];
static int cpu_threads[32];

// active window list
static HWND active_windows[512];
static uint32_t active_window_count;

// waiting thread list
static uint64_t waiting_threads[512];

// internal routines
static uint64_t ACDM_worker(void *data);
static char *GetCharFromDec(uint32_t num);
static void UpdateDateTime(void);
static void LoginWindow(void);

// static buffer ptrs
static char *static_buffers[16];

// HWNDs
static volatile HWND hwnd_error_log = 0;

// worker thread TIDs to wake when ready
static volatile uint64_t TID_start = 0;
static volatile uint64_t TID_windows = 0;
static volatile uint64_t TID_processes = 0;
static volatile uint64_t TID_resources = 0;
static volatile uint64_t TID_error = 0;
static volatile uint64_t TID_time = 0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// functions that are inited as kernel processes.

// function to update the internal date & time values
void ProcTime(void)
{
	// wait till start signal
	TID_time = __KLIB__RCGetTID();
	__KLIB__WaitThisThread();

	// thread started
	// each second update the displayed time from the system time
	while(1)
	{
		// get the minutes, hours, days, months and years
		uint32_t minutes, hours, days, months, years;

		minutes = __KLIB__SystemTimeGetMinute();
		hours = __KLIB__SystemTimeGetHour();
		days = __KLIB__SystemTimeGetDay();
		months = __KLIB__SystemTimeGetMonth();
		years = __KLIB__SystemTimeGetYear();

		// send to the ribbon controller
		UpdateRibbonTimeDate(hours, minutes, days, months, years);
		UpdateRibbons();

		// wait 3 seconds
		__KLIB__SleepThisThread(3000000);
	}


	// somehow got here
	__KLIB__KillThisProcess(-1);
}

// convert an address to a string (address has leading zeros included)
static char tmp_address[17];	// 16 chars with trailing nullchar

static char *address_to_string(uint64_t address)
{
	// number chars array
	char *numbers = "0123456789ABCDEF";

	// first setup string
	int i;
	for(i = 0; i < (sizeof(uint64_t) * 2); i++)
	{
		tmp_address[i] = '0';
	}

	// get start position (start at lowest significant portion)
	int k = sizeof(uint64_t) * 2;
	tmp_address[k] = 0x00;		// set nullchar

	// loop through, placing chars
	for(i = 0; i < (sizeof(uint64_t) * 2); i++)
	{
		// point to next char to fill
		k--;

		// get value for this nibble (4 bits)
		uint64_t value = (address >> (i * 4)) & 0x0f;

		// set the char
		tmp_address[k] = numbers[value];
	}

	return tmp_address;
}

// internal function to draw the error window
static uint64_t error_draw_lock = 0;
static void ErrorDraw(uint8_t *wnd, uint8_t *storage_buffer, uint32_t storage_index, uint32_t draw_top)
{
	// get the draw lock
	ACDMSpinEnter(&error_draw_lock);

	// get the start index in the storage buffer
	uint32_t start_index = draw_top;

	// clear the chars in the buffer
	int i, k;
	for(i = 0; i < 3680; i += 2)
	{
		wnd[i] = 0;
	}

	// copy the chars to the display buffer
	for(k = 0, i = start_index; k < 3680; k += 2)
	{
		if((k % 160) == 78*2 || (k % 160) == 79*2)
		{
			continue;
		}

		wnd[k] = storage_buffer[i++];

	}


	// finished, release draw lock
	ACDMSpinLeave(&error_draw_lock);

	// set the cursor position
	//SetCursor(hwnd_error_log & 0x1ff, (storage_index - draw_top), 1);

	// update the window
	ACDMWindowUpdate(hwnd_error_log);
	return;
}

// system error window proc
void ProcSystemError(void)
{
	// wait till start signal
	TID_error = __KLIB__RCGetTID();
	__KLIB__WaitThisThread();

	// create a message queue
	char *key_string = "I AM THE LOG QUEUE";
	MSG_CHANNEL channel = __KLIB__MSGAllocateChannel(key_string, 122);
	if(channel == 0)
	{
		__KLIB__WaitThisThread();
	}

	// bind the queue to the module
	__KLIB__MSGClientBindChannel(channel, 0xdeadbeefbabe0001);

	// create the window
	uint64_t PID = __KLIB__RCGetPID();
	HWND hwnd = ACDMCreateStaticWindow(PID, "System Log", 11);
	hwnd_error_log = hwnd;
	ACDMWindowShow(hwnd);

	// set window defaults
	// get pointer to the window buffer
	uint8_t *wnd_buffer = (uint8_t*)ACDMWindowGetBufferAddress(hwnd);

	// set the background and default chars
	int i;
	for(i = 0; i < 3680; i += 2)
	{
		wnd_buffer[i] = 0;
		wnd_buffer[i + 1] = 0x1f;
	}

	// control
	uint8_t storage_buffer[7800];	// total 100 lines
	uint8_t tmp_buffer[256];
	uint32_t storage_index = 0;
	uint32_t draw_top = 0;
	uint64_t counter = 0;

	// main program loop
	while(1)
	{
		// read in a message
		if(__KLIB__MSGServerRead(channel, tmp_buffer) != MSG_SUCCESS)
		{
			// print error and wait a bit before retrying

			__KLIB__SleepThisThread(1000000);
			continue;
		}

		// get the thread ID of the sender and the length of the string
		uint64_t TID = *((uint64_t*)&tmp_buffer[248]);
		uint32_t length = (uint32_t)tmp_buffer[247];

		// determine if it will fit in the storage buffer
		if((storage_index + length + 31) > 7800)
		{
			// reset the storage buffer
			storage_index = 0;
			draw_top = 0;

			// save the storage buffer to disk
			// TODO: fill in this bit here

			// clear the buffer
			uint64_t *dst = (uint64_t*)storage_buffer;
			int j;
			for(j = 0; j < 975; j++)
			{
				dst[j] = 0;
			}

		}

		// get a string for the TID number
		char *TID_string = address_to_string(TID);

		// need to write it to the buffer
		storage_buffer[storage_index++] = '[';
		storage_buffer[storage_index++] = '0';
		storage_buffer[storage_index++] = 'x';
		for(i = 0; i < 16; i++)
		{
			storage_buffer[storage_index++] = TID_string[i];
		}
		storage_buffer[storage_index++] = ']';
		storage_buffer[storage_index++] = ' ';
		storage_buffer[storage_index++] = '(';
		storage_buffer[storage_index++] = '0';
		storage_buffer[storage_index++] = 'x';
		TID_string = address_to_string(counter++);
		for(i = 12; i < 16; i++)
		{
			storage_buffer[storage_index++] = TID_string[i];
		}
		storage_buffer[storage_index++] = ')';
		storage_buffer[storage_index++] = ':';
		storage_buffer[storage_index++] = ' ';
		for(i = 0; i < length; i++)
		{
			storage_buffer[storage_index++] = tmp_buffer[i];
		}

		// finished, increase buffer pointer to start of next line
		if((storage_index % 78) > 0)
		{
			storage_index = ((storage_index + 78) / 78) * 78;
		}

		// change draw top
		if(storage_index >= (23 * 78))
		{
			draw_top = ((storage_index - (23 * 78)) / 78) * 78;
		}
		else
		{
			draw_top = 0;
		}

		// now need to draw it to the screen
		ErrorDraw(wnd_buffer, storage_buffer, storage_index, draw_top);

	}



	// finished
	__KLIB__WaitThisThread();
}

// start window proc
void ProcStartWindow(void)
{
	// wait till start signal
	TID_start = __KLIB__RCGetTID();
	__KLIB__WaitThisThread();

	// variable holding current selection
	int current_selection = 1;

	// loop forever
	while(1)
	{
		// set the formatting for the current selection
		char *buffer = (char*)GetStaticBufferAddress(SCR_START);

		// get a key
		KeyboardClearASCIIChar(SCR_START);
		uint8_t key_in = (uint8_t)KeyboardGetASCIIChar(SCR_START);

		// check the key type (up, down, enter)
		// and perform the necessary action
		if(key_in == 0xc2 && current_selection < 5) // down arrow
		{
			current_selection++;
		}
		else if(key_in == 0xc1 && current_selection > 1) // up arrow
		{
			current_selection--;
		}
		else if(key_in == '\n')
		{
			// return pressed. DO SOMETHING!!!
			// check each option
			if(current_selection == 3)
			{
				// switch to resources screen
				ACDMGiveFocus(SCR_RESRC);
			}
			// windows screen
			else if(current_selection == 4)
			{
				ACDMGiveFocus(SCR_WNDWS);
			}
			// processes screen
			else if(current_selection == 5)
			{
				ACDMGiveFocus(SCR_PROCS);
			}
			// system log
			else if(current_selection == 2 && hwnd_error_log)
			{
				ACDMGiveFocus(hwnd_error_log);
			}
		}


		// update display based on what just happened
		ClearCharacters(SCR_START);
		WriteStringToBuffer(SCR_START, 328, "CHOOSE AN OPTION:");
		WriteStringToBuffer(SCR_START, 328 + 320 * 1, "  - Start New Terminal Session");
		WriteStringToBuffer(SCR_START, 328 + 320 * 2, "  - View System Log");
		WriteStringToBuffer(SCR_START, 328 + 320 * 3, "  - View System Resources");
		WriteStringToBuffer(SCR_START, 328 + 320 * 4, "  - View Windows");
		WriteStringToBuffer(SCR_START, 328 + 320 * 5, "  - View Processes");
		WriteStringToBuffer(SCR_START, 328 + 320 * current_selection, ">");

		// update the video buffer
		UpdateVideoBuffer(SCR_START);


	}

	// somehow broke out of the loop. wait indefinitely
	__KLIB__WaitThisThread();
	return;
}



static char proc_states[4][16] = {"inactive", "active", "terminating", "terminated"};

static inline void DrawProcsList(uint64_t list_length, uint64_t *list_PIDs, int list_top, int list_highlighted)
{
	// clear the background
	ClearCharacters(SCR_PROCS);

	// draw the procs list
	WriteStringToBuffer(SCR_PROCS, 168,   "SELECT A PROCESS FOR MORE DETAILS -- PRESS F5 TO REFRESH");
	WriteStringToBuffer(SCR_PROCS, 484, "|       PID        |        IMAGE NAME         |   STATE    | OWNER ID |");
	WriteStringToBuffer(SCR_PROCS, 644, "------------------------------------------------------------------------");
	int i;
	for(i = 0; i < 14; i++)
	{
		// write template
		WriteStringToBuffer(SCR_PROCS, 804 + 160 * i, "|                  |                           |            |          |");

		// check for current pos in list is larger than list length
		if(i >= list_length)
		{
			continue;
		}

		// otherwise, populate this entry with the correct value
		uint64_t PID_to_print = list_PIDs[list_top + i];
		char image_name_to_print[256];
		int image_name_length = 0;
		uint32_t PID_state = 0;
		uint32_t owner_id = 0;
		__KLIB__RCGetProcDetails(PID_to_print, &PID_state, &owner_id);
		__KLIB__RCGetProcImageName(PID_to_print, image_name_to_print, &image_name_length);

		// fetch the required values
		image_name_to_print[26] = 0;
		if(image_name_length > 26)
		{
			image_name_to_print[25] = ')';
			image_name_to_print[24] = '.';
			image_name_to_print[23] = '.';
			image_name_to_print[22] = '(';
		}

		// print them
		WriteStringToBuffer(SCR_PROCS, 804 + 2 + 160 * i, "0x%a", PID_to_print);
		WriteStringToBuffer(SCR_PROCS, 804 + 42 + 160 * i, "%s", image_name_to_print);
		WriteStringToBuffer(SCR_PROCS, 804 + 98 + 160 * i, "%s", proc_states[PID_state]);
		WriteStringToBuffer(SCR_PROCS, 804 + 124 + 160 * i, "%u", owner_id);
	}
	WriteStringToBuffer(SCR_PROCS, 804 + 160 *14,  "------------------------------------------------------------------------");
	WriteStringToBuffer(SCR_PROCS, 808 + 160 *16,    "TOTAL PROCESSES:     ");
	WriteStringToBuffer(SCR_PROCS, 808 + 160 *16 + 34,    "%u", list_length);

	// set normal background
	char *background_ptr = (char*)GetStaticBufferAddress(SCR_PROCS);
	for(i = 1; i < 3680; i += 2)
	{
		*(char*)(background_ptr + i) = 0x1f;
	}

	// highlight selected proc
	char *high_ptr = (char*)(background_ptr + (805 + 160 * list_highlighted));
	for(i = 0; i < 72*2; i += 2)
	{
		high_ptr[i] = 0x8f;
	}

	// determine if scroll bar is necessary
	if(list_length > 14)
	{
		// draw the scroll bar base
		char *b_ptr = (char*)(background_ptr + 948);
		for(i = 0; i < 14; i++)
		{
			*(char*)(b_ptr + 160 * i) = 176;
		}

		// draw the scroll indicator
		// determine ratio of slots to each proc.
		double rstp = (double)14 / (double)(list_length - 14);

		// work out which slot to start on
		double start_slot = (double)list_top * rstp;

		// draw it
		int i_start_slot = (int)start_slot;
		if(i_start_slot > 13)
		{
			i_start_slot = 13;
		}
		*(char*)(b_ptr + 160 * i_start_slot) = 178;
	}

	// update the video buffer
	UpdateVideoBuffer(SCR_PROCS);

	// finished
	return;
}

static char thread_states[5][4] = {"INA", "ACT", "TRM", "SLP", "WAT"};

static inline void DrawDetailWindow(uint64_t list_length, uint64_t *list_TIDs, int list_top, int list_highlighted, uint64_t PID, char *description, uint64_t description_length, uint64_t PML4, uint64_t pages, uint64_t k_mem)
{
	// draw the base window
	int p = GenerateSubWindow(SCR_PROCS, 160 * 1 + 16, 64, 19);

	// write the process image name to the top of the window
	// get the image name
	int image_name_length = 0;
	char image_name[256];
	__KLIB__RCGetProcImageName(PID, image_name, &image_name_length);
	if(image_name_length > 60)
	{
		image_name_length = 60;
	}
	image_name[image_name_length] = 0;

	// calculate the start offset for the image name (want it center aligned ish)
	int image_name_offset = 30 - (image_name_length / 2);

	// write it
	WriteStringToBuffer(SCR_PROCS, p + image_name_offset*2, "%s", image_name);

	// draw the header
	WriteStringToBuffer(SCR_PROCS, p + 320, "|      Process Description       |      Thread ID     | STT |");

	// draw the tables
	char *ptr = (char*)GetStaticBufferAddress(SCR_PROCS);
	int i;
	for(i = 0; i < 8; i++)
	{
		ptr[i * 160 + p + 640 + 66] = '|';
		ptr[i * 160 + p + 640] = '|';
	}
	for(i = 13; i < 5 + 13; i++)
	{
		ptr[i * 160 + p + 66] = '|';
		ptr[i * 160 + p] = '|';
	}
	for(i = 0; i < 14; i++)
	{
		ptr[i * 160 + p + 120 + 640] = '|';
	}
	for(i = 34; i < 27 + 34; i++)
	{
		ptr[p + (2 * i) + 160 * 3] = '-';
	}
	for(i = 0; i < 34; i++)
	{
		ptr[p + (2 * i) + 160 * 3] = '-';
		ptr[p + (2 * i) + 160 * 12] = '-';
	}

	// draw the proc description
	for(i = 0; i < 256 && i < description_length; i++)
	{
		ptr[p + (i / 32) * 160 + (i % 32) * 2 + 642] = description[i];
	}

	// draw the visible threads
	for(i = 0; i < 14; i++)
	{
		// check for current pos in list is larger than list length
		if(i >= list_length)
		{
			continue;
		}

		// otherwise, populate this entry with the correct value
		uint64_t TID_to_print = list_TIDs[list_top + i] >> 24;
		TID_to_print = TID_to_print + (PID << 8);
		int state = list_TIDs[list_top + i] & 0xff;

		// print them
		WriteStringToBuffer(SCR_PROCS, i * 160 + p + 640 + 68, " 0x%a | %s", TID_to_print, thread_states[state]);
	}

	// highlight selected thread
	if(list_length > 0)
	{
		char *high_ptr = (char*)(ptr + (p + 709 + 160 * list_highlighted));
		for(i = 0; i < 26*2; i += 2)
		{
			high_ptr[i] = 0x8f;
		}
	}

	// print other info
	WriteStringToBuffer(SCR_PROCS, p + 160 * 13 + 4, "         Information");
	WriteStringToBuffer(SCR_PROCS, p + 160 * 14 + 4, "Threads     %u", list_length);
	WriteStringToBuffer(SCR_PROCS, p + 160 * 15 + 4, "PML4 base:  0x%a", PML4);
	WriteStringToBuffer(SCR_PROCS, p + 160 * 16 + 4, "2M Pages:   %u", pages);
	WriteStringToBuffer(SCR_PROCS, p + 160 * 17 + 4, "Kernel Mem: %u", k_mem);

	// determine if scroll bar is necessary
	if(list_length > 14)
	{
		// draw the scroll bar base
		char *b_ptr = (char*)(ptr + 978 + 122);
		for(i = 0; i < 14; i++)
		{
			*(char*)(b_ptr + 160 * i) = 176;
		}

		// draw the scroll indicator
		// determine ratio of slots to each proc.
		double rstp = (double)14 / (double)(list_length - 14);

		// work out which slot to start on
		double start_slot = (double)list_top * rstp;

		// draw it
		int i_start_slot = (int)start_slot;
		if(i_start_slot > 13)
		{
			i_start_slot = 13;
		}
		*(char*)(b_ptr + 160 * i_start_slot) = 178;
	}

	// update the video buffer
	UpdateVideoBuffer(SCR_PROCS);

	// finished
	return;
}

static inline void DrawThreadDetail(uint64_t TID, int selected)
{
	// draw the window
	int p = GenerateSubWindow(SCR_PROCS, 160 * 3 + 20, 32, 10);
	WriteStringToBuffer(SCR_PROCS, p + 16, "Thread Options");

	// draw the options
	WriteStringToBuffer(SCR_PROCS, p + 160 * 2 + 4, "  - Wake Thread");
	WriteStringToBuffer(SCR_PROCS, p + 160 * 3 + 4, "  - Kill Thread");
	WriteStringToBuffer(SCR_PROCS, p + 160 * 4 + 4, "  - Kill Process");
	WriteStringToBuffer(SCR_PROCS, p + 160 * 5 + 4, "  - Send Signal");

	// draw the selector arrow
	WriteStringToBuffer(SCR_PROCS, p + 160 * (2 + selected) + 4, ">");

	// update the video buffer
	UpdateVideoBuffer(SCR_PROCS);

	// finished
	return;
}

// procs window proc
void ProcProcsWindow(void)
{
	// wait till signal
	TID_processes = __KLIB__RCGetTID();
	__KLIB__WaitThisThread();

	// control vars
	uint64_t list_PIDs[2048];
	uint64_t list_length = 0;
	int list_selected = 0;

	// display control vars
	int list_top = 0;
	int list_highlighted = 0;

	// get the initial list & draw it
	__KLIB__RCGetProcs(list_PIDs, &list_length);
	DrawProcsList(list_length, list_PIDs, list_top, list_highlighted);

	// main list loop
	while(1)
	{
		// get key input
		uint8_t input = KeyboardGetASCIIChar(SCR_PROCS);

		// check if return key was pressed
		if(input == '\n')
		{
			// get the threads for the process
			uint64_t PID = list_PIDs[list_selected];
			uint64_t list_TIDs[512];
			uint64_t list_TID_length;
			int list_TID_top = 0;
			int list_TID_highlighted = 0;
			int list_TID_selected = 0;

			__KLIB__RCGetThreads(PID, list_TIDs, &list_TID_length);

			// get proc details (PML4 address, pages allocated and kernel memory allocated)
			uint64_t PML4 = 0;
			uint64_t pages_allocated = 0;
			uint64_t kernel_memory_allocated = 0;
			__KLIB__RCGetProcMemory(PID, &PML4, &pages_allocated, &kernel_memory_allocated);

			// get proc description
			char proc_description[256];
			uint64_t proc_description_length;
			__KLIB__RCGetProcDescription(PID, proc_description, &proc_description_length);


			// draw the initial process detail window
			DrawDetailWindow(list_TID_length, list_TIDs, list_TID_top, list_TID_highlighted, PID, proc_description, proc_description_length, PML4, pages_allocated, kernel_memory_allocated);

			// enter main loop
			while(1)
			{
				// get keyboard input
				input = KeyboardGetASCIIChar(SCR_PROCS);

				// check input
				if(input == '\n' && (PID & 0xffff) != 0 && list_TID_length != 0) // don't bring up the window if it's the IDLE thread or if the number of threads is zero
				{
					// bring up thread options window
					int list_OP_selected = 0;

					// draw the inital window
					uint64_t TID = list_TIDs[list_TID_selected];
					DrawThreadDetail(TID, list_OP_selected);

					// enter main loop
					while(1)
					{
						// get keyboard input
						input = KeyboardGetASCIIChar(SCR_PROCS);

						// check it
						if(input == '\n')
						{
							// check the option and act on it
							if(list_OP_selected == 0)
							{
							}
							else if(list_OP_selected == 1)
							{
							}
							else if(list_OP_selected == 2)
							{
							}
							else if(list_OP_selected == 3)
							{
							}

							// update the thread list
							__KLIB__RCGetThreads(PID, list_TIDs, &list_TID_length);
							list_TID_selected = 0;
							list_TID_top = 0;

							// finished, close the window
							break;
						}
						else if(input == 0x1b || input == 0x08)
						{
							break;
						}
						else if(input == 0xc2 && list_OP_selected < 3 && list_TID_length != 0) // down arrow
						{
							list_OP_selected++;
						}
						else if(input == 0xc1 && list_OP_selected > 0 && list_TID_length != 0) // up arrow
						{
							list_OP_selected--;
						}

						// draw
						DrawThreadDetail(TID, list_OP_selected);

					}

					// left main loop
					DrawDetailWindow(list_TID_length, list_TIDs, list_TID_top, list_TID_highlighted, PID, proc_description, proc_description_length, PML4, pages_allocated, kernel_memory_allocated);
					continue;
				}
				else if(input == 0x1b || input == 0x08)
				{
					break;
				}
				else if(input == 0xc2 && list_TID_selected < list_TID_length - 1) // down arrow
				{
					list_TID_selected++;
				}
				else if(input == 0xc1 && list_TID_selected > 0) // up arrow
				{
					list_TID_selected--;
				}
				else if(input == 0x94) // F5 pressed
				{
					// update the list
					__KLIB__RCGetThreads(PID, list_TIDs, &list_TID_length);
					list_TID_selected = 0;
					list_TID_top = 0;
				}

				// calculate window and highlighted row
				if(list_TID_selected >= (list_TID_top + 14))
				{
					list_TID_top++;
				}
				if(list_TID_selected < list_TID_top)
				{
					list_TID_top--;
				}

				list_TID_highlighted = list_TID_selected - list_TID_top;

				// draw
				DrawDetailWindow(list_TID_length, list_TIDs, list_TID_top, list_TID_highlighted, PID, proc_description, proc_description_length, PML4, pages_allocated, kernel_memory_allocated);
			}

			// left main loop
			DrawProcsList(list_length, list_PIDs, list_top, list_highlighted);
			continue;
		}
		else if(input == 0xc2 && list_selected < list_length - 1) // down arrow
		{
			list_selected++;
		}
		else if(input == 0xc1 && list_selected > 0) // up arrow
		{
			list_selected--;
		}
		else if(input == 0x1b || input == 0x08)	// escape or backspace
		{
		}
		else if(input == 0x94) // F5 pressed
		{
			// update the list
			__KLIB__RCGetProcs(list_PIDs, &list_length);
			list_selected = 0;
			list_top = 0;
		}

		// calculate window and highlighted row
		if(list_selected >= (list_top + 14))
		{
			list_top++;
		}
		if(list_selected < list_top)
		{
			list_top--;
		}

		list_highlighted = list_selected - list_top;

		// draw
		DrawProcsList(list_length, list_PIDs, list_top, list_highlighted);

	}

	// somehow broke out of the loop. wait indefinitely
	__KLIB__WaitThisThread();
	return;
}

// windows draw function
static inline void DrawWindowsList(uint64_t list_length, HWND *list_HWNDs, int list_top, int list_highlighted)
{
	// clear the buffer first
	ClearCharacters(SCR_WNDWS);

	// draw the windows list
	WriteStringToBuffer(SCR_WNDWS, 168,   "SELECT A WINDOW FOR MORE DETAILS --- PRESS F5 TO REFRESH");
	WriteStringToBuffer(SCR_WNDWS, 484, "|      HWND        |                  WINDOW NAME                      |");
	WriteStringToBuffer(SCR_WNDWS, 644, "------------------------------------------------------------------------");
	int i;
	for(i = 0; i < 14; i++)
	{
		// write template
		WriteStringToBuffer(SCR_WNDWS, 804 + 160 * i, "|                  |                                                   |");

		// check for current pos in list is larger than list length
		if(i >= list_length)
		{
			continue;
		}

		// otherwise, populate this entry with the correct values
		uint64_t HWND_to_print = list_HWNDs[list_top + i];
		char window_name_to_print[80];
		int window_name_length = 0;

		// get the window name
		if(HWND_to_print > 0)
		{
			char *name_ptr = GetWindowName(HWND_to_print & 0x1ff);

			// copy it accross
			int k;
			for(k = 0; k < 80 && name_ptr[k]; k++)
			{
				window_name_to_print[k] = name_ptr[k];
			}
			window_name_to_print[k] = 0;
			window_name_length = k;
		}

		// fetch the required values
		window_name_to_print[50] = 0;
		if(window_name_length > 49)
		{
			window_name_to_print[49] = ')';
			window_name_to_print[48] = '.';
			window_name_to_print[47] = '.';
			window_name_to_print[46] = '(';
		}

		// print them
		WriteStringToBuffer(SCR_WNDWS, 804 + 2 + 160 * i, "0x%a", HWND_to_print);
		WriteStringToBuffer(SCR_WNDWS, 804 + 42 + 160 * i, "%s", window_name_to_print);
	}
	WriteStringToBuffer(SCR_WNDWS, 804 + 160 *14,  "------------------------------------------------------------------------");
	WriteStringToBuffer(SCR_WNDWS, 808 + 160 *16,    "TOTAL WINDOWS:       ");
	WriteStringToBuffer(SCR_WNDWS, 808 + 160 *16 + 30,    "%u", list_length);

	// set normal background
	char *background_ptr = (char*)GetStaticBufferAddress(SCR_WNDWS);
	for(i = 1; i < 3680; i += 2)
	{
		*(char*)(background_ptr + i) = 0x1f;
	}

	// highlight selected proc
	if(list_length > 0)
	{
		char *high_ptr = (char*)(background_ptr + (805 + 160 * list_highlighted));
		for(i = 0; i < 72*2; i += 2)
		{
			high_ptr[i] = 0x8f;
		}
	}

	// determine if scroll bar is necessary
	if(list_length > 14)
	{
		// draw the scroll bar base
		char *b_ptr = (char*)(background_ptr + 948);
		for(i = 0; i < 14; i++)
		{
			*(char*)(b_ptr + 160 * i) = 176;
		}

		// draw the scroll indicator
		// determine ratio of slots to each proc.
		double rstp = (double)14 / (double)(list_length - 14);

		// work out which slot to start on
		double start_slot = (double)list_top * rstp;

		// draw it
		int i_start_slot = (int)start_slot;
		if(i_start_slot > 13)
		{
			i_start_slot = 13;
		}
		*(char*)(b_ptr + 160 * i_start_slot) = 178;
	}



	// finished, update buffer and return
	UpdateVideoBuffer(SCR_WNDWS);
	return;
}

// windows window proc
void ProcWindowsWindow(void)
{
	// wait till signal
	TID_windows = __KLIB__RCGetTID();
	__KLIB__WaitThisThread();

	// control vars
	HWND list_HWNDs[512];
	uint32_t list_length = 0;
	int list_selected = 0;

	// display control vars
	int list_top = 0;
	int list_highlighted = 0;

	// init display
	WindowGetAllActive(list_HWNDs, &list_length);
	DrawWindowsList(list_length, list_HWNDs, list_top, list_highlighted);

	// main loop
	while(1)
	{
		// get char input
		uint8_t input = KeyboardGetASCIIChar(SCR_WNDWS);

		// check if return key was pressed
		if(input == '\n' && list_length > 0)
		{
			// switch to the selected window
			ACDMGiveFocus(list_HWNDs[list_selected]);
		}
		else if(input == 0xc2 && list_selected < list_length - 1 && list_length > 0) // down arrow
		{
			list_selected++;
		}
		else if(input == 0xc1 && list_selected > 0 && list_length > 0) // up arrow
		{
			list_selected--;
		}
		else if(input == 0x1b || input == 0x08)	// escape or backspace
		{
		}
		else if(input == 0x94) // F5 pressed
		{
			// update the list
			WindowGetAllActive(list_HWNDs, &list_length);
			list_selected = 0;
			list_top = 0;
		}

		// calculate window and highlighted row
		if(list_selected >= (list_top + 14))
		{
			list_top++;
		}
		if(list_selected < list_top)
		{
			list_top--;
		}

		list_highlighted = list_selected - list_top;

		// draw
		DrawWindowsList(list_length, list_HWNDs, list_top, list_highlighted);

	}



	// somehow broke out of the loop. wait indefinitely
	__KLIB__WaitThisThread();
	return;
}


// resource window proc
void ProcResourceWindow(void)
{
	// wait till signal
	TID_resources = __KLIB__RCGetTID();
	__KLIB__WaitThisThread();



	// main loop
	while(1)
	{
		// wait until it is the active window
		// TODO finish this bit

		// grab CPU usages
		int i, k;
		for(i = 0; i < 25; i++)
		{
			cpu_activity[i] = __KLIB__RCGetCPUUsage(i);
			cpu_threads[i] = __KLIB__RCGetCPUActiveThreads(i);
		}

		// we use static buffer 'SCR_RESRC' for the resource window
		// clear the chars
		ClearCharacters(SCR_RESRC);

		// create a subwindow for the CPU usage section
		int char_offset = GenerateSubWindow(SCR_RESRC, 160 + 2, 38, 19);
		WriteStringToBuffer(SCR_RESRC, char_offset + 20, "CPU Utilisation");

		// create the CPU readings
		char_offset = char_offset + 2 + 320;
		for(i = 0; i < 32; i += 2)
		{
			// first collumn
			WriteStringToBuffer(SCR_RESRC, char_offset, "CPU %u: %u%% (%i)", i, cpu_activity[i], cpu_threads[i]);

			// second collumn
			WriteStringToBuffer(SCR_RESRC, char_offset + 36, "CPU %u: %u%% (%i)", i + 1, cpu_activity[i + 1], cpu_threads[i + 1]);
			char_offset += 160;
		}

		// create a sub-window for the memory utilisation
		char_offset = GenerateSubWindow(SCR_RESRC, 160 + 82, 37, 19);
		WriteStringToBuffer(SCR_RESRC, char_offset + 16, "Memory Utilisation");

		// update the buffer
		UpdateVideoBuffer(SCR_RESRC);

		// sleep a bit
		__KLIB__SleepThisThread(1000000);

	}

	// somehow broke out of the loop. wait indefinitely
	__KLIB__WaitThisThread();
	return;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// main entry routine
void ACDM_main(void)
{
	// spawn worker thread with stack at 0x3880000
	uint64_t worker_TID = __KLIB__SpawnNewThread(ACDM_worker, NULL, (void*)(KOFFSET + 0x3880000));
	if(worker_TID == 0)
	{
		printf("\n[ ACDM  ] ERROR: could not spawn worker thread");
		__KLIB__WaitThisThread();
	}

	// init VGA
	DisableBlink();		// disable the VGA character blink

	// main loop, check for requests and update keyboard map
	while(1)
	{
		// sleep for a bit
		__KLIB__SleepThisThread(30000);

		// update the internal keystate
		uint64_t system_keys = UpdateKeymapFromPS2();

		// get list of active windows
		WindowGetAllActive(active_windows, &active_window_count);

		// perform any window requests in the queue
		while(ACDMGetQueueLength())
		{
			// read the message
			ACDMReadMessage(msg_buffer);

			// MSG codes in qword 0 of buffer
			// 0 = switch buffer - qword 1 = HWND to switch to
			// 1 = login
			// 2 = wait till window active
			// 3 = show window
			// 4 = hide window
			// 5 = destroy window
			// 6 = update window buffer

			uint64_t *message_stream = (uint64_t*)msg_buffer;
			uint64_t mode = *message_stream;

			// check the mode
			if(mode == 1)
			{
				// need to perform the buffer switch from login screen to whatever the last buffer used was
				// switch to the previously viewed buffer (need to move this back to main proc when the message passing procedures are in place)
				current_screen = pre_lock_screen;
				SwitchBuffer(pre_lock_screen);
				UpdateVideoBuffer(pre_lock_screen);
				char *window_name = GetWindowName(pre_lock_screen);
				UpdateWindowName(window_name);
				UpdateRibbons();

			}
			else if(mode == 0)
			{
				// need to switch to the given window
				HWND hwnd_to_switch = (HWND)message_stream[1];
				current_screen = hwnd_to_switch & 0x1ff;

				// do it manually if hwnd < 16
				if(hwnd_to_switch > 16)
				{
					WindowGiveFocus(hwnd_to_switch);
				}
				else
				{
					SwitchBuffer(current_screen);
					UpdateVideoBuffer(current_screen);
					char *window_name = GetWindowName(current_screen);
					UpdateWindowName(window_name);
					UpdateRibbons();
				}

			}
			else if(mode == 2)
			{
				// get values
				HWND hwnd_to_wake = (HWND)message_stream[1];
				uint64_t TID_to_wake = message_stream[2];
				int buffer_to_wake = hwnd_to_wake & 0x1ff;

				// otherwise save the TID to the sleep slot
				waiting_threads[buffer_to_wake] = TID_to_wake;

			}
			else if(mode == 3)	// show window
			{
				WindowShow((HWND)message_stream[1]);
			}
			else if(mode == 4)	// hide window
			{
				// get the buffer for the hwnd
				HWND hwnd_to_hide = (HWND)message_stream[1];
				uint64_t buffer_to_hide = hwnd_to_hide & 0x1ff;

				// check if it is the active window
				if(current_screen == buffer_to_hide)
				{
					// move to a different screen
					// check if any other windows are active
					/*
					if(active_window_count > 1)
					{
						// move to next window down
						current_screen = active_windows[0] & 0x1ff;
					}
					else
					{
						// move to start window
						// need to switch to the start screen
						current_screen = SCR_START;
					}
					*/

					// perform the switch
					current_screen = SCR_START;

					SwitchBuffer(current_screen);
					UpdateVideoBuffer(current_screen);
					char *window_name = GetWindowName(current_screen);
					UpdateWindowName(window_name);
					UpdateRibbons();
				}

				// hide the window
				WindowHide(hwnd_to_hide);
			}
			else if(mode == 5) // destroy window
			{
				// get the window handle and buffer number
				HWND hwnd_to_destroy = (HWND)message_stream[1];
				uint64_t buffer_to_destroy = hwnd_to_destroy & 0x1ff;

				// check if it is the active window
				if(current_screen == buffer_to_destroy)
				{
					// move to a different screen
					// check if any other windows are active
					/*
					if(active_window_count > 1)
					{
						// move to next window down
						current_screen = active_windows[0] & 0x1ff;
					}
					else
					{
						// move to start window
						// need to switch to the start screen
						current_screen = SCR_START;
					}
					*/

					// perform the switch
					current_screen = SCR_START;

					SwitchBuffer(current_screen);
					UpdateVideoBuffer(current_screen);
					char *window_name = GetWindowName(current_screen);
					UpdateWindowName(window_name);
					UpdateRibbons();
				}

				// now release resources
				WindowDestroy(hwnd_to_destroy);

				// release thread waiting on the keyboard
				KeyboardPutASCIIChar(hwnd_to_destroy & 0x1ff, 0);

			}
			/*
			else if(mode == 6) // update window
			{
				// get the window handle
				HWND hwnd_to_update = (HWND)message_stream[1];

				// update the window buffer
				WindowUpdate(hwnd_to_update);
			}
			*/
			else
			{
			}

		}

		// check system_keys for any command & control combinations
		if(system_keys)
		{
			// check for caps lock on
			if(system_keys & ((uint64_t)1 << 61))		// caps lock on
			{
				// update the display
				UpdateRibbonCapsLock(1);
			}
			// caps lock off
			if(system_keys & ((uint64_t)1 << 58))		// caps lock off
			{
				// update the display
				UpdateRibbonCapsLock(0);
			}
			// numlock on
			if(system_keys & ((uint64_t)1 << 62))
			{
				UpdateRibbonNumLock(1);
			}
			// numlock off
			if(system_keys & ((uint64_t)1 << 59))
			{
				UpdateRibbonNumLock(0);
			}
			// scroll lock on
			if(system_keys & ((uint64_t)1 << 63))
			{
				UpdateRibbonScrollLock(1);
			}
			// scroll lock off
			if(system_keys & ((uint64_t)1 << 60))
			{
				UpdateRibbonScrollLock(0);
			}

			// update the ribbons
			UpdateRibbons();

			// check for super key combinations (navigation)
			if(current_screen != SCR_LOGIN)
			{
				// check for help key (F1)
				if(system_keys & 1)
				{
					// help key was pressed
					// if on help window, go back to current
					// otherwise display help window
					if(help_active)
					{
						// unset help active
						help_active = 0;

						// switch back to other screen
						current_screen = pre_help_screen;
						SwitchBuffer(pre_help_screen);
						UpdateVideoBuffer(pre_help_screen);
						char *window_name = GetWindowName(pre_help_screen);
						UpdateWindowName(window_name);
						UpdateRibbons();
					}
					else
					{
						// set the screen to return to
						pre_help_screen = current_screen;
						help_active = 1;

						// switch to help screen
						current_screen = SCR_HELPW;
						SwitchBuffer(SCR_HELPW);
						UpdateVideoBuffer(SCR_HELPW);
						char *window_name = GetWindowName(SCR_HELPW);
						UpdateWindowName(window_name);
						UpdateRibbons();
					}
				}

				// check if the help screen is not active
				if(!help_active)
				{
					// bit 0	- F1 key pressed (HELP)	*
					// bit 1	- super key released	*
					// bit 2	- super + left key released		*
					// bit 3	- super + right key released	*
					// bit 4	- super + up key released		*
					// bit 5	- super + down key released		*
					// bit 6 	- pause key
					// bit 7	- break key
					// bit 8	- print screen
					// bit 9	- super + c (close current window)
					// bit 10	- super + l (lock screen)

					// bit 57	- super held

					// check bit 1
					if(system_keys & 2)
					{
						// super key was released
						// need to switch to the start screen
						current_screen = SCR_START;
						SwitchBuffer(current_screen);
						UpdateVideoBuffer(current_screen);
						char *window_name = GetWindowName(current_screen);
						UpdateWindowName(window_name);
						UpdateRibbons();
					}
					// check bit 2 (left)
					else if(system_keys & (1 << 2))
					{
						// check if on reserved buffer ( < 16)
						if(current_screen < 16)
						{
							// switch to highest window in the list
							if(active_window_count > 0)
							{
								current_screen = active_windows[active_window_count - 1] & 0x1ff;
								SwitchBuffer(current_screen);
								UpdateVideoBuffer(current_screen);
								char *window_name = GetWindowName(current_screen);
								UpdateWindowName(window_name);
								UpdateRibbons();
							}
						}
						// otherwise on normal screen
						else
						{
							// find current window in the list
							int i;
							for(i = 0; i < active_window_count; i++)
							{
								if((active_windows[i] & 0x1ff) == current_screen)
								{
									break;
								}
							}

							// then switch to window in the position lower (if it exists)
							if(i > 0)
							{
								current_screen = active_windows[i - 1] & 0x1ff;
								SwitchBuffer(current_screen);
								UpdateVideoBuffer(current_screen);
								char *window_name = GetWindowName(current_screen);
								UpdateWindowName(window_name);
								UpdateRibbons();
							}
						}
					}
					// check bit 3 (right)
					else if(system_keys & (1 << 3))
					{
						// check if on reserved buffer ( < 16)
						if(current_screen < 16)
						{
							// switch to lowest window in the list
							if(active_window_count > 0)
							{
								current_screen = active_windows[0] & 0x1ff;
								SwitchBuffer(current_screen);
								UpdateVideoBuffer(current_screen);
								char *window_name = GetWindowName(current_screen);
								UpdateWindowName(window_name);
								UpdateRibbons();
							}
						}
						// otherwise on normal screen
						else
						{
							// find current window in the list
							int i;
							for(i = 0; i < active_window_count; i++)
							{
								if((active_windows[i] & 0x1ff) == current_screen)
								{
									break;
								}
							}

							// then switch to window in the position higher (if it exists)
							if(i + 1 < active_window_count)
							{
								current_screen = active_windows[i + 1] & 0x1ff;
								SwitchBuffer(current_screen);
								UpdateVideoBuffer(current_screen);
								char *window_name = GetWindowName(current_screen);
								UpdateWindowName(window_name);
								UpdateRibbons();
							}
						}
					}
					// check bit 4
					else if(system_keys & (1 << 4))
					{
						// up key was released while super was pressed
						// need to switch to the windows screen
						current_screen = SCR_WNDWS;
						SwitchBuffer(current_screen);
						UpdateVideoBuffer(current_screen);
						char *window_name = GetWindowName(current_screen);
						UpdateWindowName(window_name);
						UpdateRibbons();
					}
					// check bit 5
					else if(system_keys & (1 << 5))
					{
						// up key was released while super was pressed
						// need to switch to the processes screen
						current_screen = SCR_PROCS;
						SwitchBuffer(current_screen);
						UpdateVideoBuffer(current_screen);
						char *window_name = GetWindowName(current_screen);
						UpdateWindowName(window_name);
						UpdateRibbons();
					}
					// bit 9	- super + c (close current window)
					else if(system_keys & (1 << 9))
					{
					}
					// bit 10	- super + l (lock screen)
					else if(system_keys & (1 << 10))
					{
						// switch to login screen
						pre_lock_screen = current_screen;
						current_screen = SCR_LOGIN;
						SwitchBuffer(current_screen);
						UpdateVideoBuffer(current_screen);
						char *window_name = GetWindowName(current_screen);
						UpdateWindowName(window_name);
						UpdateRibbons();
					}


				}
			}

		}

		// get a formatted char (if exists) from the keystate
		char fchar = GetASCIIFromKeyState();

		// if it exists, then pass it to the active display's buffer only if no system key combos were pressed
		if(fchar && !(system_keys & ((uint64_t)1 << 57)))
		{
			// find the active display buffer
			int active_b = current_screen & 0x1ff;	// TODO: GET THE ACTIVE BUFFER. CHECK THIS

			// send the char to it
			KeyboardPutASCIIChar(active_b, fchar);
		}

		// check if a thread is waiting on the current window to get focus (EXPERIMENTAL)
		if(waiting_threads[current_screen] > 0)
		{
			// there is a thread waiting. wake it
			int e = __KLIB__WakeThreadEx(waiting_threads[current_screen]);
			if(e == WAKE_THREAD_SUCCESS)
			{
				waiting_threads[current_screen] = 0;
			}
		}


	}




	// finished, wait indefinitly
	__KLIB__WaitThisThread();
	return;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// worker thread & support functions


// function to update the login window. returns when login is successful
static void LoginWindow(void)
{
	// create a subwindow for the login
	int b = GenerateSubWindow(SCR_LOGIN, (8 * 160) + 50, 30, 5);
	WriteStringToBuffer(SCR_LOGIN, b + 14, "Administrator");
	WriteStringToBuffer(SCR_LOGIN, b + 322, "P/W:");

	// password entry field starts at char b + 322 + 10
	int cursor_pos = b + 322 + 10;
	SetCursor(SCR_LOGIN, cursor_pos / 2, 1);

	// finally, update the video buffer
	UpdateVideoBuffer(SCR_LOGIN);

	// clear the keyboard buffer
	KeyboardClearASCIIChar(SCR_LOGIN);

	// stuff needed for password control
	int pos = 0;		// starting pos of the cursor
	char pass_field[20];
	const char *password = "password";
	int password_length = 8;

	// main loop.
	while(1)
	{
		// wait for formatted ASCII input
		uint8_t input = (uint8_t)KeyboardGetASCIIChar(SCR_LOGIN);

		// check for newline char returned (indicates return pressed)
		if(input == '\n')
		{
			// disable the cursor
			int l = pos;
			pos = 0;
			SetCursor(SCR_LOGIN, 2000, 0);

			// check if given password has correct length
			if(password_length == l)
			{
				// password lengths are equal, check chars
				int correct = 1;
				int i;
				for(i = 0; i < password_length; i++)
				{
					if(password[i] != pass_field[i])
					{
						correct = 0;
						break;
					}
				}

				// check if correct
				if(correct)
				{
					// display "logging in" message
					WriteStringToBuffer(SCR_LOGIN, b + 14, "              ");
					WriteStringToBuffer(SCR_LOGIN, b + 322, "                         ");
					WriteStringToBuffer(SCR_LOGIN, b + 6, "     Logging In...");
					UpdateVideoBuffer(SCR_LOGIN);
					__KLIB__SleepThisThread(1000000);
					KeyboardClearASCIIChar(SCR_LOGIN);

					// call the kernel module to 'login'
					ACDMLogin();

					// reset the login window
					WriteStringToBuffer(SCR_LOGIN, b + 2, "                           ");
					WriteStringToBuffer(SCR_LOGIN, b + 322, "                         ");
					WriteStringToBuffer(SCR_LOGIN, b + 14, "Administrator");
					WriteStringToBuffer(SCR_LOGIN, b + 322, "P/W:");
					cursor_pos = b + 322 + 10;
					SetCursor(SCR_LOGIN, cursor_pos / 2, 1);
					UpdateVideoBuffer(SCR_LOGIN);

					// leave the login window handler
					break;
				}
				else
				{
					// display "password incorrect" message for 3 seconds before returning to password input
					WriteStringToBuffer(SCR_LOGIN, b + 14, "              ");
					WriteStringToBuffer(SCR_LOGIN, b + 322, "                         ");
					WriteStringToBuffer(SCR_LOGIN, b + 6, "Password Incorrect");
					WriteStringToBuffer(SCR_LOGIN, b + 42, ".");
					UpdateVideoBuffer(SCR_LOGIN);
					__KLIB__SleepThisThread(1000000);
					WriteStringToBuffer(SCR_LOGIN, b + 44, ".");
					UpdateVideoBuffer(SCR_LOGIN);
					__KLIB__SleepThisThread(1000000);
					WriteStringToBuffer(SCR_LOGIN, b + 46, ".");
					UpdateVideoBuffer(SCR_LOGIN);
					__KLIB__SleepThisThread(1000000);
					KeyboardClearASCIIChar(SCR_LOGIN);

					// reset to start state
					WriteStringToBuffer(SCR_LOGIN, b + 2, "                           ");
					WriteStringToBuffer(SCR_LOGIN, b + 322, "                         ");
					WriteStringToBuffer(SCR_LOGIN, b + 14, "Administrator");
					WriteStringToBuffer(SCR_LOGIN, b + 322, "P/W:");
					cursor_pos = b + 322 + 10;
					SetCursor(SCR_LOGIN, cursor_pos / 2, 1);
					UpdateVideoBuffer(SCR_LOGIN);
				}

			}
			else
			{
				// display "password incorrect" message for 3 seconds before returning to password input
				WriteStringToBuffer(SCR_LOGIN, b + 14, "              ");
				WriteStringToBuffer(SCR_LOGIN, b + 322, "                         ");
				WriteStringToBuffer(SCR_LOGIN, b + 6, "Password Incorrect");
				WriteStringToBuffer(SCR_LOGIN, b + 42, ".");
				UpdateVideoBuffer(SCR_LOGIN);
				__KLIB__SleepThisThread(1000000);
				WriteStringToBuffer(SCR_LOGIN, b + 44, ".");
				UpdateVideoBuffer(SCR_LOGIN);
				__KLIB__SleepThisThread(1000000);
				WriteStringToBuffer(SCR_LOGIN, b + 46, ".");
				UpdateVideoBuffer(SCR_LOGIN);
				__KLIB__SleepThisThread(1000000);
				KeyboardClearASCIIChar(SCR_LOGIN);

				// reset to start state
				WriteStringToBuffer(SCR_LOGIN, b + 2, "                           ");
				WriteStringToBuffer(SCR_LOGIN, b + 322, "                         ");
				WriteStringToBuffer(SCR_LOGIN, b + 14, "Administrator");
				WriteStringToBuffer(SCR_LOGIN, b + 322, "P/W:");
				cursor_pos = b + 322 + 10;
				SetCursor(SCR_LOGIN, cursor_pos / 2, 1);
				UpdateVideoBuffer(SCR_LOGIN);
			}
		}

		// check for backspace char
		else if(input == 0x08)
		{
			// check pos is not 0
			if(pos > 0)
			{
				// zero password field
				pass_field[--pos] = 0;

				// clear the asterisk at the current position
				cursor_pos -= 2;
				static_buffers[SCR_LOGIN][cursor_pos] = ' ';
				SetCursor(SCR_LOGIN, cursor_pos / 2, 1);
			}
		}

		// check if is a printable ASCII char
		else if(input >= 32 && input <= 254)
		{
			// check if password field is not full
			if(pos < 20)
			{
				// save the char to the field
				pass_field[pos++] = input;

				// print an asterisk '*'
				static_buffers[SCR_LOGIN][cursor_pos] = '*';//input;//'*';
				cursor_pos += 2;
				SetCursor(SCR_LOGIN, cursor_pos / 2, 1);
			}
		}

		// otherwise ignore it
		else
		{
		}

		// update the video buffer
		UpdateVideoBuffer(SCR_LOGIN);
	}

	// correct password has been entered.
	// transition to start screen



	return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// worker thread routine (performs built-in window background tasks)
static uint64_t ACDM_worker(void *data)
{
	// set up the ribbons
	RibbonSetup();


	// set the ribbon text
	UpdateRibbonTimeDate(12, 00, __BUILD_DAY, __BUILD_MONTH, __BUILD_YEAR);
	UpdateRibbonCapsLock(0);
	UpdateRibbonScrollLock(0);
	UpdateRibbonNumLock(0);
	UpdateBottomRibbonText("Railgun Kernel v0.4a");

	// set formatting on built-in screen buffers
	// set defaults on each screen buffer
	// clear them
	ClearBuffer(SCR_LOGIN);		// login screen
	ClearBuffer(SCR_WNDWS);		// windows screen
	ClearBuffer(SCR_START);		// start screen
	ClearBuffer(SCR_RESRC);		// resources screen
	ClearBuffer(SCR_PROCS);		// processes screen
	ClearBuffer(SCR_HELPW);		// help screen

	// get pointers to each of the built-in static buffers
	int i;
	for(i = 1; i < 16; i++)
	{
		static_buffers[i] = (char*)GetStaticBufferAddress(i);
		//printf("\nbuffer: %x at 0x%a", i, static_buffers[i]);
	}
	//printf("\n");

	// set default formatting
	for(i = 1; i < 3680; i += 2)
	{
		static_buffers[SCR_LOGIN][i] = 0x1f;	// login screen
		static_buffers[SCR_WNDWS][i] = 0x1f;	// windows screen
		static_buffers[SCR_START][i] = 0x1f;	// start screen
		static_buffers[SCR_RESRC][i] = 0x1f;	// resources screen
		static_buffers[SCR_PROCS][i] = 0x1f;	// processes screen
		static_buffers[SCR_HELPW][i] = 0x1f;	// help screen
	}

	// set default window name strings
	ChangeWindowName(SCR_LOGIN, "Login", 5);
	ChangeWindowName(SCR_HELPW, "Help", 4);
	ChangeWindowName(SCR_START, "Start", 5);
	ChangeWindowName(SCR_WNDWS, "Windows", 7);
	ChangeWindowName(SCR_RESRC, "Resources", 9);
	ChangeWindowName(SCR_PROCS, "Processes", 9);

	// setup help screen
	WriteStringToBuffer(SCR_HELPW, 328, "RAILGUN ACDM V0.8 COMMANDS:");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 1, "- SUPER        -> Start Window");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 2, "- SUPER + UP   -> Window Window");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 3, "- SUPER + DOWN -> Processes Window");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 4, "- SUPER + LEFT -> Switch Window Left");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 5, "- SUPER + RIGHT-> Switch Window Right");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 6, "- F1           -> Help/Leave Help");
	WriteStringToBuffer(SCR_HELPW, 328 + 320 * 7, "- SUPER + L    -> Lock");
	//WriteStringToBuffer(SCR_HELPW, 328 + 320 * 8, "- SUPER + C    -> Kill Window");
	WriteStringToBuffer(SCR_HELPW, 350 + 320 * 9, "Railgun OS Copyright %u, Aidan Goddard - abg10", (uint64_t)__BUILD_YEAR);
	WriteStringToBuffer(SCR_HELPW, 350 + 320 * 9 + 160, "Build %u - %u/%u/%u", (uint64_t)__BUILD_NUMBER, (uint64_t)__BUILD_DAY, (uint64_t)__BUILD_MONTH, (uint64_t)__BUILD_YEAR);

	// setup start screen defaults
	WriteStringToBuffer(SCR_START, 328, "CHOOSE AN OPTION:");
	WriteStringToBuffer(SCR_START, 328 + 320 * 1, "> - Start New Terminal Session");
	WriteStringToBuffer(SCR_START, 328 + 320 * 2, "  - View System Log");
	WriteStringToBuffer(SCR_START, 328 + 320 * 3, "  - View System Resources");
	WriteStringToBuffer(SCR_START, 328 + 320 * 4, "  - View Windows");
	WriteStringToBuffer(SCR_START, 328 + 320 * 5, "  - View Processes");


	// wake worker processes
	while(!TID_start || !TID_processes || !TID_resources || !TID_windows || !TID_error || !TID_time){}
	__KLIB__SleepThisThread(10000);
	__KLIB__WakeThreadEx(TID_start);
	__KLIB__WakeThreadEx(TID_processes);
	__KLIB__WakeThreadEx(TID_resources);
	__KLIB__WakeThreadEx(TID_windows);
	__KLIB__WakeThreadEx(TID_error);
	__KLIB__WakeThreadEx(TID_time);

	// switch to login screen
	SwitchBuffer(current_screen);
	UpdateVideoBuffer(current_screen);
	char *window_name = GetWindowName(current_screen);
	UpdateWindowName(window_name);
	UpdateRibbons();

	// main loop
	while(1)
	{
		// check current window and perform correct actions
		switch(current_screen)
		{
		case SCR_APP:
			break;

		case SCR_LOGIN:
			LoginWindow();
			break;

		default:
			__KLIB__SleepThisThread(1000000);
			break;
		}
	}

	// finished, kill this thread
	__KLIB__KillThisThread(0);
	return 0;
}
